#include "qansieditor.h"

#include "qae_qansiline.h"
#include "qae_qansibuffer.h"
#include "qae_qansidecoder.h"
#include "qae_qeditorwindow.h"
#include "qae_qansiwidget.h"
#include "qae_global.h"

#include <QtCore/QByteArray>
#include <QtCore/QTextCodec>
#include <QtCore/QTimer>
#include <QtCore/QEvent>
#include <QtCore/QPoint>
#include <QtCore/QFile>
#include <QtCore/QDebug>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtGui/QFontMetrics>
#include <QtGui/QInputMethodEvent>
#include <QtGui/QLineEdit>
#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include <QtGui/QResizeEvent>
#include <QtGui/QTableWidgetItem>
#include <QtGui/QMouseEvent>
#include <QtGui/QMoveEvent>
#include <QtGui/QWheelEvent>
#include <QMessageBox>


#ifdef Q_OS_WIN32
#include <windows.h>
#endif


namespace QAnsiEditor {


/* Maybe we should move these definition to a global class. */
#ifdef Q_OS_WIN32
UINT g_uiFormat = 0;
#endif


QAnsiWidget::QAnsiWidget(QWidget *parent, int iRow, int iCol)
        : QWidget(parent), m_iRow(iRow), m_iCol(iCol)
{
    m_asciiFont = QFont();
    m_nonAsciiFont = QFont();
    m_bBlinkVisable = true;
    m_bCursorBlinkEnabled = true;
    m_bCursorVisable = true;
    m_bCopyWithColor = true;
    m_chCurrentColor = DEFAULT_COLOR;
    m_chCurrentEffect = DEFAULT_EFFECT;

#ifdef Q_OS_WIN32
    g_uiFormat = RegisterClipboardFormatA(QGlobal::MIME_ANSITEXT);
#endif
    m_pInputCodec = QGlobal::instance()->inputEncodingCodec();
    m_pOutputCodec = QGlobal::instance()->outputEncodingCodec();

    setFocusPolicy(Qt::ClickFocus);
    setAttribute(Qt::WA_InputMethodEnabled, true);
    setAttribute(Qt::WA_OpaquePaintEvent, true);
    setMouseTracking(true);

    // Fill screen with spaces for easier implementation of selection
    m_pBuffer = new QAnsiBuffer(m_iRow, m_iCol);
    m_pBuffer->eraseEntireScreen();
    m_pBlinkTimer = new QTimer(this);
    connect(m_pBlinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
    m_pBlinkTimer->start(1000);
    m_pCursorBlinkTimer = new QTimer(this);
    connect(m_pCursorBlinkTimer, SIGNAL(timeout()), this, SLOT(cursorBlinkEvent()));
    m_pCursorBlinkTimer->start(1100);  // FIXME: since these 2 timer conflicts, so i just set different timeout values for temporary fix.

    initColors();
    initFonts();

    QPalette palette;
    palette.setColor(backgroundRole(), m_arrColors[0]);
    setPalette(palette);
}

QAnsiWidget::~QAnsiWidget()
{
    delete m_pBuffer;
}

bool QAnsiWidget::event(QEvent * e)
{
    if (e->type() == QEvent::KeyPress) {
        QKeyEvent *ke = (QKeyEvent *)e;
        if (ke->key() == Qt::Key_Tab) {
            //QEditorWindow *parent = (QEditorWindow *)parentWidget();
            //parent->keyPressEvent(ke);
            //ke->accept();
            return true;
        }
    } else if (e->type() == QEvent::ShortcutOverride) {
        QKeyEvent *ke = (QKeyEvent *)e;
        if (ke->key() == Qt::Key_W && ke->modifiers() == Qt::ControlModifier) {
            ke->accept();
            return true;
        }
    }
    return QWidget::event(e);
}

void QAnsiWidget::showEvent(QShowEvent *)
{
    update();
}

void QAnsiWidget::drawSelection()
{
    QPainter painter(this);

    QPoint ptSelStart, ptSelEnd;
    if (m_pBuffer->getSelection(&ptSelStart, &ptSelEnd)) {
        ptSelStart = mapCharToPixel(ptSelStart);
        ptSelEnd = mapCharToPixel(ptSelEnd) + QPoint(m_iCharWidth-1, m_iCharHeight-1);
        painter.fillRect(QRect(ptSelStart, ptSelEnd), Qt::gray);
    }
}

void QAnsiWidget::drawContent()
{
    QPainter painter(this);

    for (int y = 0; y < m_iRow; y++) {
        int start, end;
        if (m_pBuffer->at(y)->getChanges(&start, &end)) {
            //qDebug("y=%d, start=%d, end=%d", y, start, end);
            drawLine(painter, y, start, end);
            m_pBuffer->at(y)->clearChanges();
        }
        //drawLine(painter, y);
    }
}

void QAnsiWidget::drawCursor()
{
    QPainter painter(this);

    QPoint pt = QPoint(m_pBuffer->getCursorX(), m_pBuffer->getCursorY());
    if (m_bCursorVisable) {
        QAnsiLine *line = m_pBuffer->at(pt.y());
        QString strChar = line->getText(pt.x(), 1);
        drawChar(painter, strChar, pt.x(), pt.y(), QAnsiLine::calcCharWidth(strChar.at(0)), COLOR(0x77), MASK_BOLD, RenderAll);
    } else {
        drawLine(painter, pt.y(), pt.x(), pt.x()+1);
    }
}

void QAnsiWidget::paintEvent(QPaintEvent *pe)
{
    //QPainter painter(this);
    //painter.eraseRect(pe->rect());
    //painter.end();

    //drawSelection();
    drawContent();
    drawCursor();
}

void QAnsiWidget::resizeEvent(QResizeEvent *re)
{
    m_rcClient = QRect(1, 1, size().width()-2, size().height()-2);
    if (QGlobal::instance()->autoFont()) {
        updateFontSize();
        int iMarginH = (m_rcClient.width() - m_pBuffer->getColumnCount()*m_iCharWidth) / 2;
        int iMarginV = (m_rcClient.height() - m_pBuffer->getRowCount()*m_iCharHeight) / 2;
        m_rcClient = QRect(iMarginH, iMarginV, m_pBuffer->getColumnCount()*m_iCharWidth, m_pBuffer->getRowCount()*m_iCharHeight);
    }

    update();
}

void QAnsiWidget::keyPressEvent(QKeyEvent * e)
{
    // disable cursor blink
    setCursorBlinkEnabled(false);
    // get last text line
    QAnsiLine *lastLine = m_pBuffer->at(m_pBuffer->getCursorY());
    // key switch
    switch (e->key()) {
    case Qt::Key_Home:
        m_pBuffer->setCursor(0, m_pBuffer->getCursorY());
        break;
    case Qt::Key_End:
        m_pBuffer->setCursor(lastLine->getLength(), m_pBuffer->getCursorY());
        break;
    case Qt::Key_PageUp:
        m_pBuffer->setCursor(0, 0);
        break;
    case Qt::Key_PageDown:
        m_pBuffer->setCursor(m_pBuffer->getRowCount()-1, m_pBuffer->getColumnCount()-1);
        break;
    case Qt::Key_Up:
        m_pBuffer->moveCursor(0, -1);
        break;
    case Qt::Key_Down:
        m_pBuffer->moveCursor(0, 1);
        break;
    case Qt::Key_Left:
        if (lastLine->isPartial(m_pBuffer->getCursorX())) {
            m_pBuffer->moveCursor(-1, 0);
        } else {
            if (lastLine->isPartial(m_pBuffer->getCursorX()-1)) {
                m_pBuffer->moveCursor(-2, 0);
            }
            else {
                m_pBuffer->moveCursor(-1, 0);
            }
        }
        break;
    case Qt::Key_Right:
        if (lastLine->isPartial(m_pBuffer->getCursorX())) {
            m_pBuffer->moveCursor(1, 0);
        } else {
            if (lastLine->isPartial(m_pBuffer->getCursorX()+1)) {
                m_pBuffer->moveCursor(2, 0);
            }
            else {
                m_pBuffer->moveCursor(1, 0);
            }
        }
        break;
    case Qt::Key_Delete:
        if (lastLine->isPartial(m_pBuffer->getCursorX())) {
            m_pBuffer->moveCursor(-1, 0);
            lastLine->removeText(m_pBuffer->getCursorX(), 2);
        } else {
            if (lastLine->isPartial(m_pBuffer->getCursorX()+1)) {
                lastLine->removeText(m_pBuffer->getCursorX(), 2);
            }
            else {
                lastLine->removeText(m_pBuffer->getCursorX(), 1);
            }
        }
        break;
    case Qt::Key_Backspace:
        if (lastLine->isPartial(m_pBuffer->getCursorX())) {
            m_pBuffer->moveCursor(-1, 0);
            lastLine->removeText(m_pBuffer->getCursorX(), 2);
        } else {
            if (lastLine->isPartial(m_pBuffer->getCursorX()-1)) {
                m_pBuffer->moveCursor(-2, 0);
                lastLine->removeText(m_pBuffer->getCursorX(), 2);
            }
            else {
                m_pBuffer->moveCursor(-1, 0);
                lastLine->removeText(m_pBuffer->getCursorX(), 1);
            }
        }
        break;
    default:
        break;
    }

    // enable cursor blink
    setCursorBlinkEnabled(true);

    switch (e->key()) {
    case Qt::Key_Home:
    case Qt::Key_End:
    case Qt::Key_PageUp:
    case Qt::Key_Down:
    case Qt::Key_Left:
    case Qt::Key_Right:
    case Qt::Key_Delete:
    case Qt::Key_Backspace:
        return;
    }

    if (e->text().length()) {
        inputHandle(e->text());
    }
}

void QAnsiWidget::mousePressEvent(QMouseEvent *me)
{
    if (me->button() == Qt::LeftButton) {
        if (me->modifiers() == Qt::NoModifier) {
            // clear selection
            m_pBuffer->clearSelection();
            // set new selection
            m_bSelecting = true;
            m_ptSelStart = me->pos();
            m_ptSelEnd = m_ptSelStart;
            // move cursor
            setCursorBlinkEnabled(false);
            m_pBuffer->setCursor(mapPixelToChar(m_ptSelStart).x(), mapPixelToChar(m_ptSelStart).y());
            setCursorBlinkEnabled(true);
            update();
        }
    } else if ((me->button() == Qt::RightButton)) {
        if (me->modifiers() == Qt::NoModifier) {
			// popup menu...
        }
    }

}

void QAnsiWidget::mouseMoveEvent(QMouseEvent *me)
{
    /* QMouseEvent::button() always returns Qt::NoButton for mouse move events */
    if ((me->buttons() & Qt::LeftButton)
        && me->modifiers() == Qt::NoModifier) {
        if (m_bSelecting) {
            m_ptSelEnd = me->pos();
            QPoint ptDiff = m_ptSelEnd - m_ptSelStart;
            if (ptDiff.manhattanLength() > 3) {
                bool b = m_pBuffer->setSelection(mapPixelToChar(m_ptSelStart), mapPixelToChar(m_ptSelEnd));
                if (b) update();
            }
        }
    }
}

void QAnsiWidget::mouseReleaseEvent(QMouseEvent *me)
{
    if (me->button() == Qt::LeftButton
        && me->modifiers() == Qt::NoModifier) {
        m_bSelecting = false;
    }
}

void QAnsiWidget::blinkEvent()
{
    m_bBlinkVisable = !m_bBlinkVisable;
    for (int y = 0; y < m_iRow; y++) {
        if (m_pBuffer->at(y)->hasBlink()) {
            m_pBuffer->at(y)->setChanges(-1, -1);
        }
    }
    update();
}

void QAnsiWidget::cursorBlinkEvent()
{
    if (m_bCursorBlinkEnabled) {
        m_bCursorVisable = !m_bCursorVisable;
        //update();
    }
}

void QAnsiWidget::initColors()
{
    m_arrColors[0]  = Qt::black;
    m_arrColors[1]  = Qt::darkRed;
    m_arrColors[2]  = Qt::darkGreen;
    m_arrColors[3]  = Qt::darkYellow;
    m_arrColors[4]  = Qt::darkBlue;
    m_arrColors[5]  = Qt::darkMagenta;
    m_arrColors[6]  = Qt::darkCyan;
    m_arrColors[7]  = Qt::gray;
    m_arrColors[8]  = Qt::darkGray;
    m_arrColors[9]  = Qt::red;
    m_arrColors[10] = Qt::green;
    m_arrColors[11] = Qt::yellow;
    m_arrColors[12] = Qt::blue;
    m_arrColors[13] = Qt::magenta;
    m_arrColors[14] = Qt::cyan;
    m_arrColors[15] = Qt::white;
}

void QAnsiWidget::initFonts()
{
    QGlobal *pGlobal = QGlobal::instance();
    // ascii
    m_asciiFont.setFamily(pGlobal->asciiFont());
    m_asciiFont.setPointSize(qMax(8, 12/*m_pParam->m_nFontSize*/));
    m_asciiFont.setWeight(QFont::Normal);
    m_asciiFont.setStyleStrategy(pGlobal->antiAlias() ? QFont::PreferAntialias : QFont::NoAntialias);
    // non-ascii
    m_nonAsciiFont.setFamily(pGlobal->nonAsciiFont());
    m_nonAsciiFont.setPointSize(qMax(8, 12/*m_pParam->m_nFontSize*/));
    m_nonAsciiFont.setWeight(QFont::Normal);
    m_nonAsciiFont.setStyleStrategy(pGlobal->antiAlias() ? QFont::PreferAntialias : QFont::NoAntialias);
}

void QAnsiWidget::updateFontSize()
{
    int iInitSize = qMax(8, qMin(m_rcClient.width()*2 / m_pBuffer->getColumnCount(),
                                 m_rcClient.height() / m_pBuffer->getRowCount()));
    int iPixelSize;
    for (iPixelSize = iInitSize+4; iPixelSize >= iInitSize-4; iPixelSize--) {
        m_asciiFont.setPixelSize(iPixelSize);
        m_nonAsciiFont.setPixelSize(iPixelSize);
        calcFontMetrics();
        if ((m_pBuffer->getColumnCount()*m_iCharWidth < m_rcClient.width())
            && (m_pBuffer->getRowCount()*m_iCharHeight < m_rcClient.height())) {
            break;
        }
    }
}

void QAnsiWidget::calcFontMetrics()
{
    QFontMetrics asciiFM(m_asciiFont);
    QFontMetrics nonAsciiFM(m_nonAsciiFont);
    int en = asciiFM.width('W');
    int cn = nonAsciiFM.width(QChar(0x4e00));  // "one" in chinese
    if ((double)en / cn < 0.7) {  // almost half size
        m_iCharWidth = qMax((cn + 1) / 2, en);
    } else {
        m_iCharWidth = (qMax(en, cn) + 1) / 2;
    }

    m_iCharDelta = m_iCharWidth - cn / 2;  // always >= 0
    m_iCharHeight = asciiFM.height();
    m_iCharAscent = asciiFM.ascent();
    m_iCharDescent = asciiFM.descent();
}

void QAnsiWidget::setCursorBlinkEnabled(bool bEnable)
{
    if (bEnable) {
        m_bCursorVisable = true;
        m_bCursorBlinkEnabled = true;
        m_pCursorBlinkTimer->start();
    } else {
        if (m_pCursorBlinkTimer->isActive()) {
            m_pCursorBlinkTimer->stop();
        }
        m_bCursorVisable = true;
        m_bCursorBlinkEnabled = false;
    }

    update();
}

void QAnsiWidget::inputHandle(const QString &text)
{
    if (text.length() > 0) {
        QAnsiLine *line = m_pBuffer->at(m_pBuffer->getCursorY());
        line->setCurrentColor(m_chCurrentColor);
        line->setCurrentEffect(m_chCurrentEffect);
        int x = m_pBuffer->getCursorX();
        line->insertText(text, x);
        int len = QAnsiLine(text).getLength();
        m_pBuffer->moveCursor(len, 0);
        update();
    }
}

void QAnsiWidget::setContent(QByteArray bytes, QGlobal::Encoding encoding)
{
    QAnsiDecoder decoder(m_pBuffer, QGlobal::getEncodingCodec(encoding));
    decoder.decode(bytes.data(), bytes.length());
    update();
}

QByteArray QAnsiWidget::getContent(QGlobal::Encoding encoding)
{
    QByteArray arrRet = QByteArray();
    for (int i = 0; i < m_iRow; i++) {
        QAnsiLine *pLine = m_pBuffer->at(i);
        QByteArray arrTemp = 
            pLine->getEncodedAnsiText(QGlobal::getEncodingCodec(encoding), -1, -1, "\x1b");
        arrRet += arrTemp;
        if (i != m_iRow-1) {
            /* To be telnet-compatible, use "\r\n" here */
            arrRet += "\r\n";
        }
    }

    return arrRet;
}

// draw functions
// get blink and cursor here???
void QAnsiWidget::drawChar(QPainter &painter, const QString &strChar,
                           int iX, int iY, int iWidth, char chColor, char chEffect, RenderFlag flags)
{
    char chTempColor = chColor;
    char chTempEffect = chEffect;
    // paint
    QPoint ptPixel = mapCharToPixel(QPoint(iX, iY));
    // FIXME: seem we clear the background for many times.....
    painter.fillRect(mapCharToPixelRect(iX, iY, iWidth, 1), QBrush(m_arrColors[GETBG(chTempColor)]));
    if (!m_bBlinkVisable
        && (chTempEffect & MASK_BLINK)) {
        // noop
    } else {
        if (flags == RenderAll) {
            painter.drawText(ptPixel.x(), ptPixel.y(), m_iCharWidth*iWidth, m_iCharHeight, Qt::AlignCenter, strChar);
        } else {
            QPixmap pm = QPixmap(m_iCharWidth*2, m_iCharHeight);
            QPainter p(&pm);
            p.fillRect(0, 0, pm.width(), pm.height(), painter.background());
            p.setPen(painter.pen());
            p.setFont(painter.font());
            p.setBackground(painter.background());
            p.drawText(0, 0, m_iCharWidth*2, m_iCharHeight, Qt::AlignCenter, strChar);
            p.end();
            if (flags == RenderLeft) {
                painter.drawPixmap(ptPixel.x(), ptPixel.y(), pm, 0, 0, m_iCharWidth, m_iCharHeight);
            }
            if (flags == RenderRight) {
                painter.drawPixmap(ptPixel.x(), ptPixel.y(), pm, m_iCharWidth, 0, m_iCharWidth, m_iCharHeight);
            }
        }
    }
}

// we should draw the selection area in the whole screen before draw line !!!!!
void QAnsiWidget::drawLine(QPainter &painter, int iIndex, int iStartX, int iEndX)
{
    // validate
    Q_ASSERT(iIndex >= 0 && iIndex < m_pBuffer->getRowCount());
    QAnsiLine *pTextLine = m_pBuffer->at(iIndex);
    int iTextLength = pTextLine->getLength();
    if (iTextLength == 0) {
        return;
    }
    // normalize
    int iTempStartX = iStartX;
    int iTempEndX = iEndX;
    if (iTempStartX <= -1) {
        iTempStartX = 0;
    }
    if (iTempStartX > 0 && pTextLine->isPartial(iTempStartX)) {
        iTempStartX -= 1;
    }
    if (iTempEndX <= -1 || iTempEndX >= iTextLength) {
        iTempEndX = iTextLength - 1;
    }
    Q_ASSERT(iTempStartX >= 0 && iTempStartX < iTextLength);
    Q_ASSERT(iTempEndX >= iTempStartX && iTempEndX < iTextLength);

    char chTempColor = 0;
    char chTempEffect = 0;
    bool bSelected = false;
    bool bHighLight = false;
    QFont asciiFont = m_asciiFont;
    QFont nonAsciiFont = m_nonAsciiFont;
    for (int i = iTempStartX; i <= iTempEndX; i++) {
        chTempColor = pTextLine->getColor(i);
        chTempEffect = pTextLine->getEffect(i);

        // underline mask
        if (chTempEffect & MASK_UNDERLINE) {
            asciiFont.setUnderline(true);
            nonAsciiFont.setUnderline(true);
        } else {
            asciiFont.setUnderline(false);
            nonAsciiFont.setUnderline(false);
        }
        // set painter font
        int iCharWidth = pTextLine->getCharSize(i);
        if (iCharWidth <= 0) {  // non-printable
            continue;
        } else if (iCharWidth == 1) {
            painter.setFont(asciiFont);
        } else if (iCharWidth == 2) {
            painter.setFont(nonAsciiFont);
        }

        // bold mask
        bHighLight = chTempEffect & MASK_BOLD;
        // reverse mask
        if (chTempEffect & MASK_REVERSE) {
            chTempColor = REVERSECOLOR(chTempColor);
        }
        // override selection color
        bSelected = m_pBuffer->isSelected(QPoint(i, iIndex));
        if (bSelected) {
            chTempColor = REVERSECOLOR(DEFAULT_COLOR);
            chTempEffect = DEFAULT_EFFECT;
        }
        // set painter pen
        if (bHighLight && !bSelected) {
            painter.setPen(m_arrColors[GETFG(chTempColor)+8]);
        } else {
            painter.setPen(m_arrColors[GETFG(chTempColor)]);
        }
        // set painter background
        painter.setBackground(m_arrColors[GETBG(chTempColor)]);

        // now draw it
        QString strChar = pTextLine->getText(i, 1);
        int iHold = i;
        int iPaintWidth = iCharWidth;
        RenderFlag flags = RenderAll;
        if (iPaintWidth == 2) {
            if (pTextLine->isPartial(i)) {
                iPaintWidth = 1;
                flags = RenderRight;
            } else {
                if (chTempColor != pTextLine->getColor(i+1)
                    || chTempEffect != pTextLine->getEffect(i+1)
                    || bSelected != m_pBuffer->isSelected(QPoint(i+1, iIndex))) {
                    iPaintWidth = 1;
                    flags = RenderLeft;
                } else {
                    // RenderAll
                    i++;
                }
            }
        }
        drawChar(painter, strChar, iHold, iIndex, iPaintWidth, chTempColor, chTempEffect, flags);

    }
}

QPoint QAnsiWidget::mapCharToPixel(const QPoint& ptChar)
{
    QPoint pt = m_rcClient.topLeft();
    QPoint ptPixel;
    ptPixel.setX(ptChar.x()*m_iCharWidth + pt.x());
    ptPixel.setY(ptChar.y()*m_iCharHeight + pt.y());
    return ptPixel;
}

QPoint QAnsiWidget::mapPixelToChar(const QPoint& ptPixel)
{
    QPoint pt = m_rcClient.topLeft();
    QPoint ptChar;
    ptChar.setX(qMin(qMax(0, (ptPixel.x()-pt.x()) / m_iCharWidth), m_pBuffer->getColumnCount()-1));
    ptChar.setY(qMin(qMax(0, (ptPixel.y()-pt.y()) / m_iCharHeight), m_pBuffer->getRowCount()-1));
    return ptChar;
}

QRect QAnsiWidget::mapCharToPixelRect(int iX, int iY, int iWidth, int iHeight)
{
    QPoint pt = mapCharToPixel(QPoint(iX, iY));

    if (iWidth == -1) {  // to the end
        return QRect(pt.x(), pt.y(), size().width(), m_iCharHeight*iHeight);
    } else {
        return QRect(pt.x(), pt.y(), iWidth*m_iCharWidth, m_iCharHeight*iHeight);
    }
}

QRect QAnsiWidget::mapCharToPixelRect(const QRect &rcPixel)
{
    return mapCharToPixelRect(rcPixel.x(), rcPixel.y(), rcPixel.width(), rcPixel.height());
}

void QAnsiWidget::inputMethodEvent(QInputMethodEvent * e)
{
    QString commitString = e->commitString();
    if (!commitString.isEmpty()) {
        inputHandle(commitString);
    }
}

void QAnsiWidget::newFile()
{
    delete m_pBuffer;
    m_pBuffer = new QAnsiBuffer(m_iRow, m_iCol);
    update();
}

void QAnsiWidget::open()
{

}

void QAnsiWidget::save()
{

}

void QAnsiWidget::saveAs()
{
    QMessageBox::information(this, "Hi", "Not Implemented. :)");
}

void QAnsiWidget::undo()
{
    QMessageBox::information(this, "Hi", "Not Implemented. :)");
}

void QAnsiWidget::redo()
{
    QMessageBox::information(this, "Hi", "Not Implemented. :)");
}

void QAnsiWidget::cut()
{
    QMessageBox::information(this, "Hi", "Not Implemented. :)");
}

void QAnsiWidget::copy()
{
    QString strEscape = QGlobal::instance()->escapeString();
    QString strContent = m_pBuffer->getSelectedText(m_bCopyWithColor, strEscape);
    QTextCodec *pCodecUTF8 = QTextCodec::codecForName("UTF-8");
    QByteArray arrContentUTF8 = m_pBuffer->getEncodedSelectedText(pCodecUTF8, strEscape);

#ifdef Q_OS_WIN32
    QByteArray arrContentANSI = m_pBuffer->getEncodedSelectedText(m_pOutputCodec, strEscape);
    HWND hWnd = this->winId();
    if (OpenClipboard(hWnd)) {
        // ansi
        HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, arrContentANSI.length()+1);
        LPSTR lpstr = (LPSTR)GlobalLock(hMem);
        ZeroMemory(lpstr, arrContentANSI.length()+1);
        memcpy(lpstr, arrContentANSI.constData(), arrContentANSI.length());
        GlobalUnlock(hMem);
        SetClipboardData(CF_TEXT, hMem);
        // unicode
        hMem = GlobalAlloc(GMEM_MOVEABLE, (strContent.length()+1)*2);
        LPWSTR lpwstr = (LPWSTR)GlobalLock(hMem);
        ZeroMemory(lpwstr, (strContent.length()+1)*2);
        memcpy(lpwstr, strContent.constData(), strContent.length()*2);
        GlobalUnlock(hMem);
        SetClipboardData(CF_UNICODETEXT, hMem);
        // self-defined
        hMem = GlobalAlloc(GMEM_MOVEABLE, arrContentUTF8.length()+1);
        lpstr = (LPSTR)GlobalLock(hMem);
        ZeroMemory(lpstr, arrContentUTF8.length()+1);
        memcpy(lpstr, arrContentUTF8.constData(), arrContentUTF8.length());
        GlobalUnlock(hMem);
        SetClipboardData(g_uiFormat, hMem);
        CloseClipboard();
    }
#else
    QClipboard *clipboard = QApplication::clipboard();
    QMimeData *data = new QMimeData;
    data->setText(strContent);
    data->setData(QGlobal::MIME_ANSITEXT, arrContentUTF8);
    clipboard->setMimeData(data);
#endif  // Q_OS_WIN32
}

void QAnsiWidget::paste()
{
    QByteArray arrContent = QByteArray();
    QTextCodec *pDecodeCodec = QTextCodec::codecForName("UTF-8");

#ifdef Q_OS_WIN32
    LPSTR lpStr = NULL;
    HWND hWnd = this->winId();
    if (OpenClipboard(hWnd)) {
        HGLOBAL hMem = GetClipboardData(g_uiFormat);
        if (hMem != NULL) {
            lpStr = (LPSTR)GlobalLock(hMem);
            if (lpStr != NULL) {
                arrContent = QByteArray(lpStr);
            }
            GlobalUnlock(hMem);
        } else {
            hMem = GetClipboardData(CF_TEXT);
            if (hMem != NULL) {
                lpStr = (LPSTR)GlobalLock(hMem);
                if (lpStr != NULL) {
                    arrContent = QByteArray(lpStr);
                }
                GlobalUnlock(hMem);
            }
            pDecodeCodec = m_pInputCodec;
        }
        CloseClipboard();
    }
#else
    QClipboard *clipboard = QApplication::clipboard();
    const QMimeData *data = clipboard->mimeData();
    if (data->hasFormat(QGlobal::MIME_ANSITEXT)) {
        arrContent = data->data(QGlobal::MIME_ANSITEXT);
    } else {
        QString strContent = clipboard->text(QClipboard::Clipboard);
        arrContent = pDecodeCodec->fromUnicode(strContent);
    }
#endif  // Q_OS_WIN32

    bool insert = false;
    QAnsiBuffer buffer(m_iRow, m_iCol);
    QAnsiDecoder decoder(&buffer, pDecodeCodec);
    decoder.decode(arrContent, arrContent.length());

    for (int i = 0; i < m_iRow; i++) {
        int index = m_pBuffer->getCursorY() + i;
        if (index < m_iRow) {
            QAnsiLine *line = m_pBuffer->at(index);
            int x = m_pBuffer->getCursorX();
            if (insert) {
                line->insertAnsiText(*buffer.at(i), x);
            } else {
                line->replaceAnsiText(*buffer.at(i), x);
            }
        }
        //qDebug() << i << ": " << qPrintable(buffer.at(i)->getAnsiText());
    }
    update();

}

void QAnsiWidget::colorChanged(char chColor)
{
    m_chCurrentColor = COLOR(chColor);
    if (chColor & 0x08) {  // highlight
        m_chCurrentEffect |= MASK_BOLD;
    } else {
        m_chCurrentEffect &= ~MASK_BOLD;
    }
    m_pBuffer->setAttributeOfSelectedText(m_chCurrentColor, m_chCurrentEffect);
}

void QAnsiWidget::blinkToggled(bool bBlink)
{
    if (bBlink) {
        m_chCurrentEffect |= MASK_BLINK;
    } else {
        m_chCurrentEffect &= ~MASK_BLINK;
    }
    m_pBuffer->setAttributeOfSelectedText(-1, m_chCurrentEffect);
}

void QAnsiWidget::underlineToggled(bool bUnderline)
{
    if (bUnderline) {
        m_chCurrentEffect |= MASK_UNDERLINE;
    } else {
        m_chCurrentEffect &= ~MASK_UNDERLINE;
    }
    m_pBuffer->setAttributeOfSelectedText(-1, m_chCurrentEffect);
}

void QAnsiWidget::charClicked(QTableWidgetItem *item)
{
    inputHandle(item->text());
}

QByteArray QAnsiWidget::parseString(const QByteArray& cstr, int *len)
{
    QByteArray parsed = "";

    if (len != 0)
        *len = 0;

    for (int i = 0; i < cstr.length(); i++) {
        if (cstr.at(i) == '^') {
            i++;
            if (i < cstr.length()) {
                parsed += CTRL(cstr.at(i));
                if (len != 0)
                    *len = *len + 1;
            }

        } else
            if (cstr.at(i) == '\\') {
                i++;
                if (i < cstr.length()) {
                    if (cstr.at(i) == 'n')
                        parsed += CHAR_CR;
                    else if (cstr.at(i) == 'r')
                        parsed += CHAR_LF;
                    else if (cstr.at(i) == 't')
                        parsed += CHAR_HT;
                    if (len != 0)
                        *len = *len + 1;
                }
            } else {
                parsed += cstr.at(i);
                if (len != 0)
                    *len = *len + 1;
            }
    }

    return parsed;
}


}  // namespace QAnsiEditor
